package org.dreamwork.dsi.embedded.httpd.support;

import org.dreamwork.dsi.embedded.httpd.annotation.AWebHandler;
import org.dreamwork.dsi.embedded.httpd.annotation.AWebMapping;
import org.dreamwork.injection.IInjectResolvedProcessor;
import org.dreamwork.injection.IObjectContext;
import org.dreamwork.util.StringUtil;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Resource
public class WebHandlerScanner implements IInjectResolvedProcessor {
//    private final Logger logger = LoggerFactory.getLogger (WebHandlerScanner.class);
    private final Map<String, WebHandlerMapping> cache = new HashMap<> ();

    private final Pattern PATTERN = Pattern.compile ("^/?(.*?)(/(.*?))?$");

/*
    @Resource
    private IObjectContext context;
*/

    @Override
    public void perform (IObjectContext context) /*throws Exception */{
        for (String beanName : context.getAllBeanNames ()) {
            Object bean = context.getBean (beanName);
            Class<?> type = bean.getClass ();
            System.out.println ("---------> " + type.getCanonicalName ());
            if (type.isAnnotationPresent (AWebHandler.class)) {
                AWebHandler awh = type.getAnnotation (AWebHandler.class);
                String category = awh.value ();
                if (StringUtil.isEmpty (category)) {
                    throw new IllegalArgumentException ("Invalid category");
                }
                category = category.trim ();
                if (cache.containsKey (category)) {
                    throw new IllegalStateException ("category has register for bean: ");
                }

                WebHandlerMapping mapping = new WebHandlerMapping (category, beanName);
                cache.put (category, mapping);

                Method[] methods = type.getMethods ();
                for (Method method : methods) {
                    if (method.isAnnotationPresent (AWebMapping.class)) {
                        AWebMapping awm = method.getAnnotation (AWebMapping.class);
                        String httpMethod = awm.method ();
                        if (StringUtil.isEmpty (httpMethod)) {
                            httpMethod = "get";
                        }
                        String pattern = awm.pattern ();
                        if (StringUtil.isEmpty (pattern)) {
                            pattern = awm.value ();
                        }
                        if (StringUtil.isEmpty (pattern)) {
                            pattern = "/";
                        }

                        WebMappedMethod wmm = new WebMappedMethod (method, pattern);
                        wmm.contentType = awm.contentType ();

                        switch (httpMethod.toLowerCase ()) {
                            case "get":
                                if (mapping.get.containsKey (pattern)) {
                                    throw new IllegalArgumentException ("pattern " + pattern + " already mapped.");
                                }
                                mapping.get.put (pattern, wmm);
                                break;
                            case "post":
                                if (mapping.post.containsKey (pattern)) {
                                    throw new IllegalArgumentException ("pattern " + pattern + " already mapped.");
                                }
                                mapping.post.put (pattern, wmm);
                                break;
                            case "delete":
                                if (mapping.delete.containsKey (pattern)) {
                                    throw new IllegalArgumentException ("pattern " + pattern + " already mapped.");
                                }
                                mapping.delete.put (pattern, wmm);
                                break;
                            case "put":
                                if (mapping.put.containsKey (pattern)) {
                                    throw new IllegalArgumentException ("pattern " + pattern + " already mapped.");
                                }
                                mapping.put.put (pattern, wmm);
                                break;
                            default:
                                throw new IllegalArgumentException ("http method: " + httpMethod + " does not support.");
                        }
                    }
                }
            }
        }
    }

    public WebHandler match (String pathInfo, String method, Map<String, String> parsedArgs) {
        Matcher m = PATTERN.matcher (pathInfo);
        if (!m.matches ()) {
            return null;
        }

        String category = m.group (1);
        if (!cache.containsKey (category)) {
            return null;
        }

        String pattern = m.group (2);
        if (StringUtil.isEmpty (pattern)) {
            pattern = "/";
        }
        WebHandlerMapping mapping = cache.get (category);
        Map<String, WebMappedMethod> map;
        switch (method) {
            case "get"    : map = mapping.get;    break;
            case "post"   : map = mapping.post;   break;
            case "delete" : map = mapping.delete; break;
            case "put"    : map = mapping.put;    break;
            default: return null;
        }

        for (WebMappedMethod wmm : map.values ()) {
            if (pattern.equals (wmm.pattern)) { // 精确命中
                return new WebHandler (mapping.beanName, wmm);
            }
        }
        for (WebMappedMethod wmm : map.values ()) {
            if (wmm.matches (pattern, parsedArgs)) {        // 更复杂的情况
                return new WebHandler (mapping.beanName, wmm);
            }
        }
        return null;
    }
}
